
The example programs
   FrameBufferImplementations.java
   PixelArray.java
   PixelArray_v2.java
   PixelArray_v3.java
   ViewportInFramebufferSimulation.java
are meant to be used with the "Java Visualizer" web site. These
programs do not use the framebuffer package. Instead, they "simulate"
very simplified definitions of the FrameBuffer and Viewport classes.
The simplified definitions make it easier to understand and visualize
the more complex implementations used in the framebuffer package.
Also see
   https://blogs.oracle.com/javamagazine/post/java-array-objects

The interface to the FrameBuffer class implies that a FrameBuffer
object is a two-dimensional array of pixels (or colors). But the
implementation of the FrameBuffer class does not store its data
in a two-dimensional array. The implementation stores its data in
a one-dimensional, row major, array of integers. The examples in
this folder give you a visual idea of why we implement a frameBuffer
as a one-dimensional array of int (and not as a two-dimensional array).

Java (and C++) implement a two-dimensional array as a one-dimensional
array of one-dimensional arrays (this is often referred to as an
"array-of-rows").

When you declare a two-dimensional array reference variable,

   int[][] arr;

it must refer to a one-dimensional array of one-dimensional integer
arrays (an array-or-rows, or an array-of-arrays).

   arr = new int[4][]; // arr is a 1-dimensional array of 4 int arrays.

At this point, arr is a one-dimensional array (with length four) that is
"empty" (all four of its entries are the value null). The next line of
code puts a one-dimensional array (of length five) in the first entry
of arr.

   arr[0] = new int[5];

The next line of code puts a one-dimensional array in the second entry
of arr.

   arr[1] = new int[5];

The next two lines "complete" the array with two more rows, each with
length five.

   arr[2] = new int[5];
   arr[3] = new int[5];

The array now looks like a two-dimensional table of numbers, but from
the point of view of Java, it is a one-dimensional array where each
entry in the array is itself a one-dimensional array.

   [ [0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0] ]

But here is a diagram that represents how Java stores this data in its heap.

        int[][]            int[4][]                int[5]
       +------+           +-------+             +-------------------+
   arr |  o---|---------->|  o----|------------>| 0 | 0 | 0 | 0 | 0 |
       +------+           +-------+             +-------------------+
                          |  o----|--------+
                          +-------+        |       int[5]
                          |  o----|-----+  |    +-------------------+
                          +-------+     |  +--->| 0 | 0 | 0 | 0 | 0 |
                          |  o----|--+  |       +-------------------+
                          +-------+  |  |
                                     |  |          int[5]
                                     |  |       +-------------------+
                                     |  +------>| 0 | 0 | 0 | 0 | 0 |
                                     |          +-------------------+
                                     |
                                     |             int[5]
                                     |          +-------------------+
                                     +--------->| 0 | 0 | 0 | 0 | 0 |
                                                +-------------------+

Notice that the array is broken up into five objects in the heap (notice
in the code above how there are five "new" operations in the building of
this array). These five objects need not be stored in memory locations that
are near to each other. When an array like this needs to be transferred from
one location in memory to another (for example, when it needs to be sent to
a GPU), gathering up all the objects from all the different locations in
memory takes up quite a bit of time. On the other hand, the row-major
one-dimensional version of this array looks like the following diagram.
The array data is contiguous in memory and can be moved (to a GPU) with
great speed and efficiency.

        int[]              int[20]
       +------+           +-------------------------------------------------------------------------------+
   arr |  o---|---------->| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
       +------+           +-------------------------------------------------------------------------------+

This is the reason we use a row-major one-dimensional array to implement
a framebuffer. The row-major one-dimensional array provides a far more
efficient implementation of the framebuffer than a two-dimensional array.

Question: Explain why

   Color[][] pixels = new Color[4][5];

will end up being 25 distinct objects in the heap (draw a picture).


There is a good reason to create a two-dimensional array as an
arrays-of-row. It allows us to create a non-rectangular array.

This code,

   int[][] b = new int[5][];
   b[0] = new int[1];
   b[1] = new int[2];
   b[2] = new int[3];
   b[3] = new int[4];
   b[4] = new int[5];

creates a "triangular" array.

   [[0],
    [0, 0],
    [0, 0, 0],
    [0, 0, 0, 0]
    [0, 0, 0, 0, 0]]

One way to think about Java's two-dimensional "array-of-rows" is that
they are similar to a "list of lists".

   List<List<Integer>> table;  // A two-dimensional table of integers.


So far we have explained why a FrameBuffer stores its pixel data as a
row-major one-dimensional array. What about a Viewport, how does it
store its pixel data? The short answer is that a Viewport does not
store any pixel data. A Viewport represents a sub-rectangle of pixels
from a FrameBufer. The Viewport should rely on the FrameBuffer to store
the pixel data for that sub-rectangle and the Viewport should communicate
with its FrameBuffer when the Viewport wants to set or get some pixel data.

The interesting question now is how does a Viewport object communicate with
its FrameBuffer object? The only way that any two objects can communicate
is if one of them contains a reference to the other. We need to explain how
a Viewport object contains a reference to its FrameBuffer object.

We create a FrameBuffer object like this,

   FrameBuffer fb = new FrameBuffer(400, 400);

and we create a Viewport in that FrameBuffer like this,

   FrameBuffer.Viewport vp = fb.new Viewport(50, 50, 200, 200);

Notice how odd that syntax is. The full name of the Viewport class is
FrameBufer.Viewport. When we want to instantiate a Viewport object we
have to use the "new" operator in an unusual way, fb.new. Both of these
extra dots are a consequence of how the Viewport class is defined. It is
defined as an "inner class" inside of the FrameBuffer class. The name of
the class is FrameBuffer.Viewport because the Viewport class is a public
field in the FrameBuffer class, just as a method or a variable are public
fields in a class.

Here is a brief (but compilable) outline of the FrameBuffer class and its
nested inner Viewport class. Notice how the definition of the Viewport
class sits inside of the FrameBuffer class kind of like a method. And just
as methods have access to all the other methods and fields of a class, the
Viewport has access to all the methods and fields of the FrameBuffer class.
Notice how the setPixelVP() and getPixelVP() methods make use of the
FrameBuffer's get and set methods for pixels (and therefore the FrameBuffer's
pixel_buffer array).

   class FrameBuffer
   {
      public final int widthFB;   // Instance variables.
      public final int heightFB;
      public final int[] pixel_buffer;

      public FrameBuffer(int widthFB, int heightFB)
      {
         this.widthFB = widthFB;
         this.heightFB = heightFB;
         this.pixel_buffer = new int[widthFB * heightFB];
      }

      public void setPixelFB(int x, int y, Color c)
      {
         pixel_buffer[y*widthFB + x] = c.getRGB();
      }

      public Color getPixelFB(int x, int y)
      {
         return new Color( pixel_buffer[y*widthFB + x] );
      }

      public class Viewport  // Inner class.
      {
         public final int ul_x;  // Upper left-hand corner in FrameBuffer.
         public final int ul_y;
         public final int widthVP;
         public final int heightVP;

         public Viewport(int ul_x, int ul_y, int widthVP, int heightVP)
         {
            this.ul_x = ul_x;
            this.ul_y = ul_y;
            this.widthVP = widthVP;
            this.heightVP = heightVP;
         }

         public void setPixelVP(int x, int y, Color c)
         {
            setPixelFB(ul_x + x, ul_y + y, c);
         }

         public Color getPixelVP(int x, int y)
         {
            return getPixelFB(ul_x + x, ul_y + y);
         }
      }
   }

When a Viewport object is instantiated, the Viewport object will contain
a reference to the FrameBuffer object that the new operator was called on.
Then the methods in the Viewport object can access the methods and fields
of its FrameBuffer object.

https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
https://dev.java/learn/classes-objects/nested-classes/
https://www.baeldung.com/java-nested-classes
